use actix_web::web;

use crate::{
    database::{table, view, DbPool},
    route::form::{
        image::NewImageForm,
        post::{NewPostForm, PostForm, PreviewPostForm, ViewPostForm, WritePostForm},
    },
    service::{class, ServiceError},
    srv_add, srv_del, srv_get_by_slug, srv_get_in_page, srv_get_in_page_by_foreign_key,
    srv_get_in_page_by_slug, srv_upd, utils,
};

srv_add!(add, NewPostForm, table::post::crud::create);
srv_upd!(upd, PostForm, table::post::crud::update);
srv_del!(del, table::post::crud::delete);

srv_get_by_slug!(get_by_sid, PostForm, table::post::crud::read_by_sid);
srv_get_in_page!(
    get_with_author_in_page,
    PreviewPostForm,
    view::post_with_author::read_count_from_offset
);
srv_get_in_page_by_foreign_key!(
    get_in_page_by_tag_id,
    PostForm,
    table::post::crud::read_count_from_offset_by_tid
);
srv_get_in_page_by_slug!(
    get_in_page_by_tag_slug,
    PostForm,
    class::tag::get_by_slug,
    id,
    get_in_page_by_tag_id
);

pub async fn write(pool: web::Data<DbPool>, form: WritePostForm) -> Result<String, ServiceError> {
    let sid = utils::generate_post_id();
    let pid = add(pool.clone(), form.to(&sid)).await?;
    let images = form.images;
    let image_forms = images
        .into_iter()
        .map(|image| NewImageForm { pid, url: image })
        .collect();
    class::image::add_images(pool.clone(), image_forms).await?;
    Ok(sid)
}

pub async fn view(pool: web::Data<DbPool>, sid: String) -> Result<ViewPostForm, ServiceError> {
    let post = get_by_sid(pool.clone(), sid).await?;
    let author = class::tag::get_id(pool.clone(), post.tid).await?;
    let tags = class::tag::get_id_in(pool.clone(), post.tids.clone()).await?;
    let links = class::link::get_by_tag_id(pool.clone(), author.id).await?;
    let images = class::image::get_by_post_id(pool.clone(), post.id)
        .await?
        .into_iter()
        .map(|image| image.url)
        .collect();
    Ok(ViewPostForm {
        post,
        tags,
        author,
        links,
        images,
    })
}

pub async fn delete(pool: web::Data<DbPool>, pid: i32) -> Result<i32, ServiceError> {
    let images = class::image::get_by_post_id(pool.clone(), pid).await?;
    class::image::del_images(pool.clone(), images).await?;
    del(pool.clone(), pid).await?;
    Ok(pid)
}
